/*LICENSE_BEGIN
 * ******************************************************************
 * Copyright 2006 by the Dainamite Robocup-Team at the
 * DAI-Labor, TU-Berlin (www.dai-labor.de)
 * 
 * Members of Dainamite are:
 * Holger Endert
 * Robert Wetzker
 * Thomas Karbe
 * Axel Heßler
 * Philippe Bütter
 * Felix Brossmann
 * 
 * Licensed under the GNU GPL (General Public License) Free Software 
 * License, Version 2. You may use this file only in compliance with 
 * the License. You may obtain a copy of the License at:
 * 
 * http://www.fsf.org/licensing/licenses/gpl.txt
 * ******************************************************************
LICENSE_END*/
package robocup.connection;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import robocup.component.RobocupAgent;

/**
 * This class is used as communication interface to SoccerScope. In order to
 * avoid hard dependencies to soccerscope, this has been realized using java
 * reflection (no import instructions are neccessary) and the mediator and
 * singleton patterns. This allows to detach soccerscope easiliy from the framework.
 * <br><br>
 * Note: Do not make any changes unless you know what you do here! 
 */
public class TeamSoccerScopeMediator {

	/**
	 * Reference to the soccerscope object
	 */
	private static Object soccerScopeInstance = null;

	/**
	 * A reflected method for adding heared messages to soccerscope.
	 */
	private static Method soccerScopeAddHearMessageToScene = null;

	/**
	 * A reflected method for adding said messages to soccerscope.
	 */
	private static Method soccerScopeAddSayMessageToScene = null;

	/**
	 * A reflected class for the registration of an agent to soccerscope.
	 * <code>Register.java</code>
	 */
	private static Class registerClass = getRegisterClass();

	/**
	 * A reflected method for adding state-assessments to soccerscope.
	 */
	private static Method registerAddStateAss3 = getRegisterAddStateAss3();

	/**
	 * A reflected method for register an agent to soccerscope. Used with
	 * <code>registerClass</code>.
	 */
	private static Method registerPlayerBean = getRegisterPlayerBean();

	/**
	 * A reflected method for adding received infos to soccerscope.
	 */
	private static Method registerInfoReceived = getRegisterInfoReceived();

	/**
	 * A reflected method for adding a hole to soccerscope.
	 */
	private static Method registerHole = getRegisterHole();

	/**
	 * This is the unidirectional interface from the robocup framework to
	 * soccerscope. Sending all data to soccerscope (world-model data of the
	 * agents) is done by calling some methods via reflection.
	 */
	private TeamSoccerScopeMediator() {
	}

	/**
	 * This method creates a method by reflection, which is later used to
	 * add send messages (via say) to a scene of soccerscope.
	 * 
	 * @return the corresponding method
	 */
	private static Method getSoccerScopeAddSayMessageToScene() {
		Method m = null;
		try {
			m = soccerScopeInstance.getClass().getMethod(
					"addSayMessageToScene", Integer.TYPE, String.class);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		return m;
	}

	/**
	 * This method creates a method by reflection, which is later used to
	 * add heared messages to a scene of soccerscope.
	 * 
	 * @return the corresponding method
	 */
	private static Method getSoccerScopeAddHearMessageToScene() {
		Method m = null;
		try {
			m = soccerScopeInstance.getClass().getMethod(
					"addHearMessageToScene", Integer.TYPE, String.class);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		return m;
	}

	/**
	 * This method creates a method by reflection, which is later used to
	 * add holes of an agent to a scene of soccerscope.
	 * 
	 * @return the corresponding method
	 */
	private static Method getRegisterHole() {
		Method m = null;
		try {
			m = registerClass.getMethod("hole", RobocupAgent.class,
					Integer.TYPE);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		return m;
	}

	/**
	 * This method creates a method by reflection, which is later used to
	 * add a received info to a scene of soccerscope.
	 * 
	 * @return the corresponding method
	 */
	private static Method getRegisterInfoReceived() {
		Method m = null;
		try {
			m = registerClass.getMethod("infoReceived", RobocupAgent.class);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		return m;
	}

	/**
	 * This method creates a method by reflection, which is later used to
	 * register an agent at soccerscope, which is a precondition for adding
	 * world-model data later on.
	 * 
	 * @return the corresponding method
	 */
	private static Method getRegisterPlayerBean() {
		Method m = null;
		try {
			m = registerClass.getMethod("registerPlayerBean",
					RobocupAgent.class);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		return m;
	}

	/**
	 * This method creates a method by reflection, which is later used to
	 * add a state-assessment to a scene of soccerscope.
	 * 
	 * @return the corresponding method
	 */
	private static Method getRegisterAddStateAss3() {
		Method m = null;
		try {

			m=registerClass.getMethod("addStateAss",String.class,RobocupAgent.class);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		return m;
	}


	/**
	 * This method creates a reflected class of the type <code>
	 * soccerscope.daistatistic.Register</code>. This is used to register agents
	 * to soccerscope.
	 * 
	 * @return the register class
	 */
	private static Class getRegisterClass() {
		Class c = null;
		try {
			c = Class.forName("soccerscope.daistatistic.Register");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		return c;
	}

	/**
	 * This method returns the complete SoccerScope instance.
	 * 
	 * @return the monitor main class
	 */
	private static Object getSoccerScopeInstance() {
		Object o = null;

		Class c;
		try {
			c = Class.forName("soccerscope.SoccerScope");
			Method m = c.getMethod("getInstance", (Class[]) null);
			o = m.invoke(c, (Object[]) null);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}

		return o;
	}


	/**
	 * Add state assessements to history. Used by the agents.
	 * 
	 * @param s the StateInfo
	 * @param wm the worldmodel of the player agent
	 */
	public static void registerAddStateAss(String s, RobocupAgent agent) {
		try {

			registerAddStateAss3.invoke(registerClass,s,agent);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}


	/**
	 * Method to register an agent at soccerscope. Used therefore 
	 * by the agents
	 * 
	 * @param ra - the robocup-agent.
	 */
	public static void registerPlayerBean(RobocupAgent ra) {
		try {
			registerPlayerBean.invoke(registerClass, ra);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}


	/**
	 * Method for adding received infos to soccerscope. This is used by the 
	 * agents.
	 * 
	 * @param ra - the robocup-agent
	 */
	public static void registerInfoReceived(RobocupAgent ra) {
		try {
			registerInfoReceived.invoke(registerClass, ra);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Method to register a hole for an agent at soccerscope. Used by the agent.
	 * 
	 * @param ra - the robocup-agent
	 * @param numberOfNewHoles - the amount of holes to register
	 */
	public static void registerHole(RobocupAgent ra, int numberOfNewHoles) {
		try {
			registerHole.invoke(registerClass, ra,
					new Integer(numberOfNewHoles));
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	/**
	 * This method is used to add heared messages to a scene of soccerscope.
	 * Used by the agents.
	 * 
	 * @param pIndex - the index of the player agent.
	 * @param buf - the communicated messages (via hear-command)
	 */
	public static void soccerScopeAddHearMessageToScene(int pIndex, String buf) {
		try {
			soccerScopeAddHearMessageToScene.invoke(soccerScopeInstance,
					new Integer(pIndex), buf);
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Method to add spoken messages to soccerscope. Used by the agents.
	 * 
	 * @param index - the index of the player agent
	 * @param msg - the communicated message (via say-command).
	 */
	public static void soccerScopeAddSayMessageToScene(int index, String msg) {
		if (soccerScopeInstance == null)
			soccerScopeInstance = getSoccerScopeInstance();
		try {
			soccerScopeAddSayMessageToScene.invoke(soccerScopeInstance,
					new Integer(index), msg);

		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	/**
	 * This method sets the default host of soccerscope, which is used to 
	 * automatically connect to the server.
	 * 
	 * @param host - the host-name of the server
	 */
	public static void soccerServerConnectionDefaultHost(String host) {
		try {
			Class c = Class.forName("soccerscope.net.SoccerServerConnection");
			c.getField("DEFAULT_HOST").set(c, host);

			soccerScopeInstance = getSoccerScopeInstance();
			soccerScopeAddHearMessageToScene = getSoccerScopeAddHearMessageToScene();
			soccerScopeAddSayMessageToScene = getSoccerScopeAddSayMessageToScene();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}
	}
}
